﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
using System.Diagnostics;

using OpenTK;
using OpenTK.Graphics.OpenGL;
using GraphicsF;

namespace test
{
    public partial class WaveModulation : Form
    {
        private int
            x = 0,
            y = 0,
            z = 0,
            modelcount = 0;
        float 
            width = 20;
        bool
            loader = false,
            calculate = false,
            click = false,
            right = false,
            left = false,
            stop = false,
            start = false,
            loker = false;

        List<Modeling.Point> res = new List<Modeling.Point>();
        Thread CalculateProcess;

        double
            Wmax = 0,
            Wmin = 0,
            W2 = 0;

        public WaveModulation()
        {
            this.StartPosition = FormStartPosition.CenterScreen;
            InitializeComponent();
        }

        public void Slow()
        {
            GL.MatrixMode(MatrixMode.Modelview);
            GL.Rotate(1, 0, 0, 1);
            OpenGLControl.Invalidate();
        }

        private void CBNet_CheckedChanged(object sender, EventArgs e)
        {
            UpdateGLControl();
        }
        private void UpdateGLControl()
        {
            GL.MatrixMode(MatrixMode.Modelview);
            GL.Rotate(5, 1, 0, 0);
            GL.Rotate(-5, 1, 0, 0);
            OpenGLControl.Invalidate();
        }
        private void OpenGLControl_Load(object sender, EventArgs e)
        {
            loader = true;
            GL.ClearColor(Color.Black);
            GL.Enable(EnableCap.DepthTest);

            Matrix4 p = Matrix4.CreatePerspectiveFieldOfView((float)(100 * Math.PI / 180), 1, 20, 500);
            GL.MatrixMode(MatrixMode.Projection);
            GL.LoadMatrix(ref p);

            Matrix4 modelview = Matrix4.LookAt(100, 30, 0, 0, 0, 0, 0, 1, 0);
            GL.MatrixMode(MatrixMode.Modelview);
            GL.LoadMatrix(ref modelview);
        }
        private void OpenGLControl_Paint(object sender, PaintEventArgs e)
        {
            if (!loader)
                return;

            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

            GL.Clear(ClearBufferMask.DepthBufferBit | ClearBufferMask.ColorBufferBit);
            if (CBOrdinate.Checked)
                GF.ShowOrdenate();
            if (CBNet.Checked)
                GF.ShowNet();
            if (start)
            {
                //if (calculate)
                //    CalculateProcess.Suspend();
                loker = true;
                var worklist = Clone(res);
                loker = false;
                GF.BuildStruct(worklist, Wmax, Wmin, W2, TBZoom.Value);
                //if (calculate)
                //    CalculateProcess.Resume();
            }
            //GF.ShowLimit(TBZoom.Value, Wmax - Wmin, W2 - Wmin);
            OpenGLControl.SwapBuffers();
        }
        public static List<T> Clone<T>(List<T> listToClone) where T : Modeling.Point
        {
            return listToClone.Select(item => (T)item.Clone()).ToList();
        }
        private void OpenGLControl_Scroll(object sender, ScrollEventArgs e)
        {
            //z = e.NewValue - z;
            GL.MatrixMode(MatrixMode.Projection);
            GL.Rotate(1, 0, 0, 1);
            OpenGLControl.Invalidate();
        }

        private void OpenGLControl_KeyDown(object sender, KeyEventArgs e)
        {
            if (!loader) return;

            if (e.KeyCode == Keys.Q || e.KeyCode == Keys.Up)
            {
                GL.MatrixMode(MatrixMode.Modelview);
                GL.Rotate(5, 0, 0, -1);
            }
            if (e.KeyCode == Keys.E || e.KeyCode == Keys.Down)
            {
                GL.MatrixMode(MatrixMode.Modelview);
                GL.Rotate(5, 0, 0, 1);
            }
            if (e.KeyCode == Keys.Z || e.KeyCode == Keys.Left)
            {
                GL.MatrixMode(MatrixMode.Modelview);
                GL.Rotate(5, 1, 0, 0);
            }
            if (e.KeyCode == Keys.C || e.KeyCode == Keys.Right)
            {
                GL.MatrixMode(MatrixMode.Modelview);
                GL.Rotate(5, -1, 0, 0);
            }

            OpenGLControl.Invalidate();

        }

        private void OpenGLControl_Resize(object sender, EventArgs e)
        {
            int w = OpenGLControl.Width;
            int h = OpenGLControl.Height;
            if (this.WindowState == FormWindowState.Maximized)
            {
                w = h + 50;
                GL.Viewport(300, 0, w, h);
            }
            else
                GL.Viewport(0, 0, w, h);
        }

        private void OpenGLControl_MouseDown(object sender, MouseEventArgs e)
        {
            click = true;
            if (e.Button == System.Windows.Forms.MouseButtons.Left)
                left = true;
            else
                right = true;
            x = e.X;
            y = e.Y;
            z = 0;
        }

        private void OpenGLControl_MouseMove(object sender, MouseEventArgs e)
        {
            if (!click || calculate)
                return;
            MatrixMode mode = new MatrixMode();
            if (left)
                mode = MatrixMode.Modelview;
            else
                mode = MatrixMode.Projection;
            if (x - e.X < 1)
            {
                GL.MatrixMode(mode);
                GL.Rotate(1, 0, 1, 0);
                x = e.X;
            }
            else
            {
                GL.MatrixMode(mode);
                GL.Rotate(1, 0, -1, 0);
                x = e.X;
            }
            if (y - e.Y < 1)
            {
                GL.MatrixMode(mode);
                GL.Rotate(1, 1, 0, -1);
                y = e.Y;
            }
            else
            {
                GL.MatrixMode(mode);
                GL.Rotate(1, -1, 0, 1);
                y = e.Y;
            }
            OpenGLControl.Invalidate();
        }

        private void OpenGLControl_MouseUp(object sender, MouseEventArgs e)
        {
            click = false;
            right = false;
            left = false;
        }

        private void BtStart_Click(object sender, EventArgs e)
        {
            if (modelcount == 2)
            {
                modelcount = 0;
                res.Clear();
            }
            if (!calculate)
            {
                start = true;
                if (CheckData())
                {
                    PBModeling.Value = 0;
                    LbProgress.Text = "Моделювання...";
                    LbProgress.Update();
                    CalculateProcess = new Thread(delegate() { GetData(double.Parse(TBFerrite.Text), double.Parse(TBDielectric1.Text), double.Parse(TBDielectric2.Text), double.Parse(TBMagneticField.Text)); });
                    CalculateProcess.Name = "Build";
                    CalculateProcess.Start();
                }
                UpdateGLControl();
            }
        }

        private void BtClear_Click(object sender, EventArgs e)
        {
            res.Clear();
            modelcount = 0;
            start = false;
            UpdateGLControl();
        }

        private void CBOrdinate_CheckedChanged(object sender, EventArgs e)
        {
            UpdateGLControl();
        }
        private bool CheckData()
        {
            if (TBFerrite.Text != string.Empty &&
                TBDielectric1.Text != string.Empty &&
                TBDielectric2.Text != string.Empty &&
                TBMagneticField.Text != string.Empty)
                return true;
            return false;
        }
        private void GetData(double S1, double S2, double S3, double H0)
        {
            calculate = true;
            S1 = S1 * Math.Pow(10, -6);
            S2 = S2 * Math.Pow(10, -6);
            S3 = S3 * Math.Pow(10, -6);
            double
                M0 = 1750,
                Gama = 2.8 * Math.Pow(10, 6),
                Wm = Gama * M0,
                Wh = Gama * H0,
                Wh_2 = Math.Pow(Wh, 2),
                W1 = Math.Sqrt(Wh * (Wh + Wm)),
                W1_2 = Wh * (Wh + Wm),

                SensTheta = Math.PI / TBThetaSensitivity.Value,
                SensW = ((Wh + Wm) - W1) / 1000,
                SensKs = 10 / S1 / TBKsSensitivity.Value;

            Wmax = Wh + Wm;
            Wmin = W1;
            W2 = Wh + Wm / 2;
            int count = 0;
            PBModeling.Maximum = int.Parse(((2 * Math.PI / 360) * TBTheta.Value / SensTheta).ToString().Split(new char[] {','})[0]) + 1;
            for (double theta = 0; theta < (2 * Math.PI / 360) * TBTheta.Value; theta += SensTheta)
            {
                count = res.Count;
                for (double ks = 0; ks < 10 / Math.Pow(10, -4.5); ks += SensKs)
                {
                    double
                        ks_2 = Math.Pow(ks, 2),
                        ky = ks * Math.Sin(theta),
                        ky_2 = Math.Pow(ky, 2),
                        kz = ks * Math.Cos(theta),
                        kz_2 = Math.Pow(kz, 2),
                        prevRes = 1;
                    for (double w = W1; w <= Wh + Wm * 5; w += SensW)
                    {
                        if (stop)
                        {
                            PBModeling.Value = PBModeling.Maximum;
                            LbProgress.Text = "Зупинено";
                            stop = false;
                            calculate = false;
                            return;
                        }
                        double
                            w_2 = Math.Pow(w, 2),
                            m = (w_2 - W1_2) / (w_2 - Wh_2),
                            m_2 = Math.Pow(m, 2),
                            ma = w * Wm / (w_2 - Wh_2),
                            ma_2 = Math.Pow(ma, 2),
                            k = Math.Sqrt(kz_2 / m + ky_2),
                            k_2 = kz_2 / m + ky_2,
                            eS1 = Math.Pow(Math.E, 2 * ks * S1),
                            eS23 = Math.Pow(Math.E, 2 * ks * (S2 + S3)),
                            eS13 = Math.Pow(Math.E, 2 * ks * (S1 + S3)),
                            eS2 = Math.Pow(Math.E, 2 * ks * S2);

                        //Math.Pow(Math.E, -ks * (S1 + S2 + S3)) * 
                        double funcRes = (2 * (eS1 - eS23) * k * ks * m * Math.Cosh(k * S1) -
                            (eS1 * (ks_2 + k_2 * m_2 - ky_2 * ma_2) + eS23 * (ks_2 + k_2 * m_2 - ky_2 * ma_2) - eS2 * (ks_2 - k_2 * m_2 - 2 * ks * ky * ma + ky_2 * ma_2) - eS13 * (ks_2 - k_2 * m_2 + 2 * ks * ky * ma + ky_2 * ma_2)) * Math.Sinh(k * S1));
                        //double funcRes = Math.Pow(Math.E, -ks*S1) * (2 * ks * m * k * Math.Cosh(k * S1) + (m_2 * k_2 + ks_2 - ma_2 * ky_2) * Math.Sinh(k * S1));
                        if ((prevRes * funcRes < 0) && !double.IsInfinity(funcRes))
                        {
                            if (loker)
                                while (loker == true) ;
                            res.Add(new Modeling.Point(w, theta, ks, modelcount));
                            break;
                        }
                        else if (prevRes < 0)
                            break;
                        else
                            prevRes = funcRes;
                    }
                }
                TBUpperLimit.Text = (Wmax / Math.Pow(10, 9)).ToString().Substring(0, 3);
                TBLowerLimit.Text = (Wmin / Math.Pow(10, 9)).ToString().Substring(0, 3);
                TBWidthOfTheRange.Text = ((Wmax - Wmin) / Math.Pow(10, 9)).ToString().Substring(0, 3);
                PBModeling.Value++;
                OpenGLControl.Invalidate();
            }
            PBModeling.Value = PBModeling.Maximum;
            LbProgress.Text = "Готово";
            calculate = false;
            modelcount++;
        }

        private void TBTheta_Scroll(object sender, EventArgs e)
        {
            LbTheta.Text = TBTheta.Value.ToString();
        }

        private void trackBar1_Scroll(object sender, EventArgs e)
        {
            OpenGLControl.Invalidate();
        }

        private void BtStop_Click(object sender, EventArgs e)
        {
            stop = true;
        }

        private void TBThetaSensitivity_Scroll(object sender, EventArgs e)
        {
            LbThetaSensitivityValue.Text = (TBThetaSensitivity.Value - 36).ToString();
        }

        private void TBKsSensitivity_Scroll(object sender, EventArgs e)
        {
            LbKsSensitivityValue.Text = (TBKsSensitivity.Value - 100).ToString();
        }
    }
}
